home *** CD-ROM | disk | FTP | other *** search
Text File | 2000-09-28 | 8.7 KB | 298 lines | [TEXT/CWIE] |
- /*
- File: OTSimpleDownloadHTTP.c
-
- Contains: Implementation of the simple HTTP download sample.
-
- Written by: Quinn "The Eskimo!"
-
- Copyright: Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 7/23/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
-
- /////////////////////////////////////////////////////////////////////
- // The OT debugging macros in <OTDebug.h> require this variable to
- // be set.
- #include <Files.h>
- #include <TextUtils.h>
-
- #ifndef qDebug
- #define qDebug 1
- #endif
-
- /////////////////////////////////////////////////////////////////////
- // Pick up all the standard OT stuff.
-
- #include <OpenTransport.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up all the OT TCP/IP stuff.
-
- #include <OpenTptInternet.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up the OTDebugBreak and OTAssert macros.
-
- #include <OTDebug.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up YieldToAnyThread.
-
- #include <Threads.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up our own prototype.
-
- #include "OTSimpleDownloadHTTP.h"
-
- /////////////////////////////////////////////////////////////////////
- // OTDebugStr is not defined in any OT header files, but it is
- // exported by the libraries, so we define the prototype here.
-
- extern pascal void OTDebugStr(const char* str);
-
- /////////////////////////////////////////////////////////////////////
-
- enum {
- kTransferBufferSize = 4096
- };
-
- /////////////////////////////////////////////////////////////////////
-
- static pascal void YieldingNotifier(void* contextPtr, OTEventCode code,
- OTResult result, void* cookie)
- // This simple notifier checks for kOTSyncIdleEvent and
- // when it gets one calls the Thread Manager routine
- // YieldToAnyThread. Open Transport sends kOTSyncIdleEvent
- // whenever it's waiting for something, eg data to arrive
- // inside a sync/blocking OTRcv call. In such cases, we
- // yield the processor to some other thread that might
- // be doing useful work.
- {
- #pragma unused(contextPtr)
- #pragma unused(result)
- #pragma unused(cookie)
- OSStatus junk;
-
- switch (code) {
- case kOTSyncIdleEvent:
- junk = YieldToAnyThread();
- OTAssert("YieldingNotifier: YieldToAnyThread failed", junk == noErr);
- break;
- default:
- // do nothing
- break;
- }
- }
-
- /////////////////////////////////////////////////////////////////////
-
- OSStatus DownloadHTTPSimple(const char *hostName,
- const char *httpCommand,
- const short destFileRefNum)
- // Download a URL from the a web server. hostName is a pointer
- // to a string that contains the DNS address of the web server.
- // The DNS address must be suffixed by ":<port>", where <port>
- // is the port number the web server is operating on.
- // httpCommand contains the HTTP command to send. Typically this
- // is of the form:
- //
- // GET <x> HTTP/1.0\0x0d\0x0a\0x0d\0x0a
- //
- // where <x> is the URL path. destFileRefNum is the file
- // reference number to which the results of the HTTP command
- // are written. This routine does not parse the returned HTTP
- // header in any way. The entire incoming stream goes into
- // the file verbatim.
- //
- // For example, if you were asked to download a URL like:
- //
- // http://devworld.apple.com/dev/technotes.shtml
- //
- // you would set:
- //
- // o hostName to "devworld.apple.com:80" (80 is the
- // default port for HTTP.
- // o httpCommand to "GET /dev/technotes.shtml HTTP/1.0\0x0d\0x0a\0x0d\0x0a"
- {
- OSStatus err;
- OSStatus junk;
- Ptr transferBuffer = nil;
- EndpointRef ep = kOTInvalidEndpointRef;
- TCall sndCall;
- DNSAddress hostDNSAddress;
- OTFlags junkFlags;
- OTResult bytesSent;
- OTResult bytesReceived;
- OTResult lookResult;
- Boolean bound = false;
-
- // First allocate a buffer for storing the data as we read it.
-
- err = noErr;
- transferBuffer = OTAllocMem(kTransferBufferSize);
- if ( transferBuffer == nil ) {
- err = kENOMEMErr;
- }
-
- // Now open a TCP endpoint.
-
- if (err == noErr) {
- ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err);
- }
-
- // If the endpoint opens successfully...
-
- if (err == noErr) {
-
- // Establish the modes of operation. This sample uses
- // sync/blocking mode, with sync idle events that yield
- // time using the Thread Manager.
-
- junk = OTSetSynchronous(ep);
- OTAssert("DownloadHTTPSimple: OTSetSynchronous failed", junk == noErr);
-
- junk = OTSetBlocking(ep);
- OTAssert("DownloadHTTPSimple: OTSetBlocking failed", junk == noErr);
-
- junk = OTInstallNotifier(ep, YieldingNotifier, nil);
- OTAssert("DownloadHTTPSimple: OTInstallNotifier failed", junk == noErr);
-
- junk = OTUseSyncIdleEvents(ep, true);
- OTAssert("DownloadHTTPSimple: OTUseSyncIdleEvents failed", junk == noErr);
-
- // Bind the endpoint. Because we're an outgoing connection,
- // we don't have to bind it to a specific address.
-
- err = OTBind(ep, nil, nil);
- bound = (err == noErr);
- }
-
- // Initialise the sndCall structure and call OTConnect. We nil
- // out most of the fields in the sndCall structure because
- // we don't want any special options or connection data.
- // The important field of the sndCall is the addr TNetBuf,
- // which we initialise to the
-
- if (err == noErr) {
- sndCall.addr.buf = (UInt8 *) &hostDNSAddress;
- sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostName);
- sndCall.opt.buf = nil; // no connection options
- sndCall.opt.len = 0;
- sndCall.udata.buf = nil; // no connection data
- sndCall.udata.len = 0;
- sndCall.sequence = 0; // ignored by OTConnect
-
- err = OTConnect(ep, &sndCall, nil);
- }
-
- // Send the HTTP command to the server.
-
- if (err == noErr) {
- bytesSent = OTSnd(ep, (void *) httpCommand, OTStrLength(httpCommand), 0);
-
- // OTSnd returns the number of bytes sent. Because we're in
- // synchronous mode, it won't return until it's sent all the
- // bytes, or it gets an error.
-
- if (bytesSent > 0) {
- err = noErr;
- } else {
- err = bytesSent;
- }
- }
-
- // Now that we have sent the HTTP command, we turn around and
- // receive the data comming back from the server.
-
- if (err == noErr) {
- do {
- bytesReceived = OTRcv(ep, (void *) transferBuffer, kTransferBufferSize, &junkFlags);
-
- // OTRcv returns the number of bytes received. Because we're in
- // synchronous mode, it won't return until it's sent all the
- // bytes, or it gets an error.
-
- if (bytesReceived > 0) {
- err = FSWrite(destFileRefNum, &bytesReceived, transferBuffer);
- } else {
- err = bytesReceived;
- }
-
- // We keep running this loop until we get an error.
-
- } while (err == noErr);
- }
-
- // Now we handle the various forms of error we can get. The
- // most common in kOTLookErr. This means that some event
- // has happened that we need to look at. We call OTLook
- // to get the event code and then handle the various types
- // of event appropriately.
-
- if (err == kOTLookErr) {
-
- lookResult = OTLook(ep);
-
- switch (lookResult) {
-
- case T_DISCONNECT:
- // If we get a T_DISCONNECT event, the remote peer
- // has disconnected the stream in a dis-orderly
- // fashion. HTTP servers will often just disconnect
- // a connection like this to indicate the end of the
- // data, so all we need do is clear the T_DISCONNECT
- // event on the endpoint.
-
- err = OTRcvDisconnect(ep, nil);
- break;
-
- case T_ORDREL:
- // If we get a T_ORDREL event, the remote peer
- // has disconnected the stream in an orderly
- // fashion. This orderly disconnect indicates that
- // the end of the data. We respond by clearing
- // the T_ORDREL, and then calling OTSndOrderlyDisconnect
- // to acknowledge the orderly disconnect at
- // the remote peer.
-
- err = OTRcvOrderlyDisconnect(ep);
- if (err == noErr) {
- err = OTSndOrderlyDisconnect(ep);
- }
- break;
-
- default:
- // Leave err as kOTLookErr.
- break;
- }
- }
-
- if ( (err == noErr) && bound ) {
- junk = OTUnbind(ep);
- OTAssert("DownloadHTTPSimple: OTUnbind failed.", junk == noErr);
- }
-
- // Clean up.
- if (ep != kOTInvalidEndpointRef) {
- junk = OTCloseProvider(ep);
- OTAssert("DownloadHTTPSimple: OTCloseProvider failed.", junk == noErr);
- }
- if (transferBuffer != nil) {
- OTFreeMem(transferBuffer);
- }
-
- return (err);
- }
-